﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;
using System.Windows.Media.Animation;
using TraceView.Service;
using TraceView.Shared;
using TraceView.UI.CustomControls;
using TraceView.UI.Entities;

using Transitionals.Transitions;

namespace TraceView.UI
{
    public partial class TraceViewer : IDisplayTrace, INotifyPropertyChanged
    {
        #region Private Fields
        //List of registered applications and the currently-selected application
        private readonly ApplicationCollection _applications;
        private ApplicationInfo _selectedApplication;

        //List of filters
        private readonly CategoryCollection _categories;
        private readonly SeverityCollection _severities;
        private readonly UserCollection _users;
        private readonly Profiles _profiles;
        private readonly Listeners _listeners;

        private readonly TraceServiceHost _serviceHost;

        private bool _canNavigateToMainPage;
        private bool _canShowNotification;
        private object _currentView;
        private bool _isSettingsViewLoaded;
        private int _itemSize = 2;
        private int _level = 1;
        private int _masterListLevel = 1;
        private int _maxDuration;
        private int _maxItems;
        private object _previousView;
        private TraceItem _selectedTraceItem;
        private Color _themeColor;
        private string _statusText = "Server Running";
        private ImageSource _serverImage;
        private string _searchString;
      
        private Profile _newProfile;
        private bool _isGeneratingMode;
        
        #endregion

        #region Properties
        public ApplicationCollection Applications
        {
            get { return _applications; }
        }

        public CategoryCollection Categories
        {
            get { return _categories; }
        }

        public SeverityCollection Severities
        {
            get { return _severities; }
        }

        public UserCollection Users
        {
            get { return _users; }
        }

        public int Level
        {
            get { return _level; }
            set { SetProperty(() => Level, ref _level, value); }
        }

        public object CurrentView
        {
            get { return _currentView; }
            set { SetProperty(() => CurrentView, ref _currentView, value); }
        }

        public object PreviousView
        {
            get { return _previousView; }
            set { SetProperty(() => PreviousView, ref _previousView, value); }
        }    

        public int ItemSize
        {
            get { return _itemSize; }
            set { SetProperty(() => ItemSize, ref _itemSize, value); }
        }

        public ApplicationInfo SelectedApplication
        {
            get { return _selectedApplication; }
            set { SetProperty(() => SelectedApplication, ref _selectedApplication, value); }
        }

        public TraceItem SelectedTraceItem
        {
            get { return _selectedTraceItem; }
            set { SetProperty(() => SelectedTraceItem, ref _selectedTraceItem, value); }
        }

        public bool CanNavigateToMainPage
        {
            get { return _canNavigateToMainPage; }
            set { SetProperty(() => CanNavigateToMainPage, ref _canNavigateToMainPage, value); }
        }

        public Color ThemeColor
        {
            get { return _themeColor; }
            set { SetProperty(() => ThemeColor, ref _themeColor, value); }
        }

        public string StatusText
        {
            get { return _statusText; }
            set { SetProperty(() => StatusText, ref _statusText, value); }
        }

        public ImageSource ServerImage
        {
            get { return _serverImage; }
            set { SetProperty(() => ServerImage, ref _serverImage, value); }
        }
      
        public int MaxItems
        {
            get { return _maxItems; }
            set
            {
                SetProperty(() => MaxItems, ref _maxItems, value);
            }
        }

        public bool CanShowNotification
        {
            get { return _canShowNotification; }
            set
            {
                SetProperty(() => CanShowNotification, ref _canShowNotification, value);
            }
        }

        public int MaxDuration
        {
            get { return _maxDuration; }
            set
            {
                SetProperty(() => MaxDuration, ref _maxDuration, value);
            }
        }

        public string SearchString
        {
            get { return _searchString; }
            set
            {
                SetProperty(() => SearchString, ref _searchString, value);
            }
        }

        public Profile NewProfile
        {
            get { return _newProfile; }
            set
            {
                SetProperty(() => NewProfile, ref _newProfile, value);
            }
        }

        public bool IsGeneratingMode
        {
            get { return _isGeneratingMode; }
            set
            {
                SetProperty(() => IsGeneratingMode, ref _isGeneratingMode, value);
            }
        }

        public Profiles Profiles
        {
            get { return _profiles; }
        }

        public Listeners Listeners
        {
            get { return _listeners; }
        }
        public string Theme { get; set; }
        #endregion

        #region Events
        public static readonly RoutedEvent OnNewItemEvent = EventManager.RegisterRoutedEvent("OnNewItem",
            RoutingStrategy.Bubble, typeof(TraceEventHandler), typeof(TraceViewer));

        public event TraceEventHandler OnNewItem
        {
            add { AddHandler(OnNewItemEvent, value); }
            remove { RemoveHandler(OnNewItemEvent, value); }
        }
        #endregion

        #region Constructor
        public TraceViewer()
        {
            InitializeComponent();

            _serviceHost = new TraceServiceHost(this);

            _severities = new SeverityCollection();
            _categories = new CategoryCollection();
            _users = new UserCollection();
            _profiles = new Profiles();
            _listeners  = new Listeners();

            _profiles.Add(new Profile {Name = "Test App",CreationDate=DateTime.Now});
            _profiles.Add(new Profile { Name = "Visual Studio", CreationDate = DateTime.Now });
            _profiles.Add(new Profile { Name = "Expression Blend", CreationDate = DateTime.Now });

            _applications =
                new ApplicationCollection(new IFilterCollectionMatchCriteria[] { _severities, _categories, _users });
            ServerImage = TryFindResource("Green") as ImageSource;
            PopulateSeverityData();
        }

        private void PopulateSeverityData()
        {
            Severities.Add(new SeverityInfo(Severity.Critical));
            Severities.Add(new SeverityInfo(Severity.Error));
            Severities.Add(new SeverityInfo(Severity.Warning));
            Severities.Add(new SeverityInfo(Severity.Verbose));
            Severities.Add(new SeverityInfo(Severity.Information));
        }
        #endregion

        #region IDisplayTrace Members
        public void ShowTrace(TraceItem item)
        {
            if (btnPause.IsChecked.GetValueOrDefault(false))
                return;           

            var userControl = item.View as BaseHandlerView;
            if (userControl != null)
                userControl.Owner = this;
            int applicationCount = Applications.Count;
            Applications.Add(item);
            if (applicationCount == 0)
            {
                SelectedApplication = Applications[0];
                if (!_isSettingsViewLoaded)
                {
                    Container.Transition = null;
                    CurrentView = grdBlank;
                    Container.Transition = new FadeTransition {
                        Duration = new TimeSpan(0, 0, 0, 1)
                    };
                    CurrentView = grdMainView;
                } else
                    PreviousView = grdMainView;
            }
            Users.Add(new UserInfo(item.Source) {
                Name = item.UserName
            });
            foreach (string category in item.Categories)
                Categories.Add(new CategoryInfo(category));
            if (OnNewItemEvent != null && CanShowNotification)
            {
                RaiseEvent(new TraceEventArgs(item) {
                    RoutedEvent = OnNewItemEvent
                });
            }
            if (SelectedApplication != null && string.Compare(SelectedApplication.ApplicationName, item.ApplicationName) != 0)
                Applications.FirstOrDefault(app => string.Compare(app.ApplicationName, item.ApplicationName) == 0).
                    NewItemsCount++;
        }
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;

        private void SetProperty<T>(Expression<Func<T>> property, ref T backingField, T value)
        {
            backingField = value;
            NotifyPropertyChanged(property);
        }

        private void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
        {
            if (PropertyChanged == null)
                return;

            var lambda = (LambdaExpression)property;
            var unaryExpression = lambda.Body as UnaryExpression;
            MemberExpression memberExpression = unaryExpression != null
                ? (MemberExpression)unaryExpression.Operand : (MemberExpression)lambda.Body;
            PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
        }
        #endregion

        #region Event Handlers
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            grdContainer.Children.Remove(grdMainView);
            grdContainer.Children.Remove(grdDetailView);
            grdContainer.Children.Remove(grdOptionsPage);
            grdContainer.Children.Remove(grdBlank);
            grdContainer.Children.Remove(grdHomePage);
            CurrentView = grdHomePage;
            LoadState();
        }

        private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
        {
            SaveState();
            _serviceHost.Close();
            StatusText = "Server stopped.";
        }

        private void OnBtnBackClicked(object sender, RoutedEventArgs e)
        {
            Level = _masterListLevel;
            Container.Transition = null;
            CurrentView = grdBlank;
            Container.Transition = new TranslateTransition();
            CurrentView = grdMainView;
        }

        private void OnListBoxMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var element = ((ListBox)(sender));
            ShowTraceItemDetails((TraceItem)element.SelectedItems[0]);
        }

        private void OnLaunchDetailsClick(object sender, RoutedEventArgs e)
        {
            var element = ((FrameworkElement)(sender));

            ShowTraceItemDetails((TraceItem)element.DataContext);

        }

        private void OnItemSizeChanged(object sender, RoutedEventArgs e)
        {
            var rb = (ImageRadioButton)sender;
            if (rb.Tag != null)
                Level = Convert.ToInt32(rb.Tag);
        }

        private void ApplicationStackSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedApplication.NewItemsCount = 0;
        }

        private void ItemStackSelectionChanged(object sender, SelectionChangedEventArgs e)
        {           
            //NotifyPropertyChanged(() => FilteredTraceItems);           
        }

        private void OnBtnSettingsClick(object sender, RoutedEventArgs e)
        {
            GetSavedProfiles();
            _isSettingsViewLoaded = true;
            PreviousView = CurrentView;
            Container.Transition = null;
            CurrentView = grdBlank;
            Container.Transition = new FadeAndGrowTransition();
            CurrentView = grdOptionsPage;
        }

        private void OnBtnSettingsBackClicked(object sender, RoutedEventArgs e)
        {
            Container.Transition = null;
            CurrentView = grdBlank;
            Container.Transition = new TranslateTransition();
            CurrentView = PreviousView;
            _isSettingsViewLoaded = false;
        }

        private void BtnDeleteClick(object sender, RoutedEventArgs e)
        {
            var element = ((FrameworkElement)(sender));
            if (element.DataContext is TraceItem)
            {
                var item = (TraceItem) element.DataContext;
                SelectedApplication.TraceItems.Remove(item);
                SelectedApplication.AllItems.Remove(item);
                SelectedApplication.RecentItems.Remove(item);
                SelectedApplication.PinnedItems.Remove(item);

                if(CurrentView == grdDetailView)
                 OnBtnBackClicked(null,null);
                //if (ItemStack.SelectedItem == TabRecentItems)
                //    SelectedApplication.RecentItems.Remove((TraceItem) element.DataContext);
                //else if (ItemStack.SelectedItem == TabPinnedItems)
                //    SelectedApplication.PinnedItems.Remove((TraceItem)element.DataContext);
                //else if (ItemStack.SelectedItem == TabAllItems)
                //    SelectedApplication.AllItems.Remove((TraceItem)element.DataContext);
            }                        
        }

        private void OnBtnCopyClick(object sender, RoutedEventArgs e)
        {
             var element = ((FrameworkElement)(sender));
             if (element.DataContext is TraceItem)
             {
                 ((TraceItem)element.DataContext).CopyToClipBoard();
             }
        }

        private void ButtonAboutClick(object sender, RoutedEventArgs e)
        {
            CanNavigateToMainPage = true;
            Container.Transition = null;
            CurrentView = grdBlank;
            Container.Transition = new FadeAndGrowTransition();
            CurrentView = grdHomePage;
        }

        private void OnBtnAboutBackClicked(object sender, RoutedEventArgs e)
        {
            Container.Transition = null;
            CurrentView = grdBlank;
            Container.Transition = new TranslateTransition();
            CurrentView = grdMainView;
            CanNavigateToMainPage = false;
        }

        private void ThemeColorChecked(object sender, RoutedEventArgs e)
        {
            var btn = (ImageRadioButton)sender;
            Theme = btn.Tag.ToString();
            switch (btn.Tag.ToString())
            {
                case "default":
                    ThemeColor = Color.FromRgb(0X1D, 0X1B, 0X1B);
                    break;
                case "blue":
                    ThemeColor = Color.FromRgb(0X03, 0X48, 0X6E);
                    break;
                case "pink":
                    ThemeColor = Color.FromRgb(0XB7, 0X27, 0X50);
                    break;
                case "green":
                    ThemeColor = Color.FromRgb(0X03, 0X27, 0X03);
                    break;
                case "yellow":
                    ThemeColor = Color.FromRgb(0XCF, 0X8B, 0X00);
                    break;
            }
            NotifyPropertyChanged(() => ThemeColor);
        }

        private void ShowTraceItemDetails(TraceItem traceItem)
        {
            _masterListLevel = Level;
            Level = 4;
            SelectedTraceItem = traceItem;
            Container.Transition = new FlipTransition {
                Duration = new TimeSpan(0, 0, 1)
            };
            CurrentView = grdDetailView;
        }

        private void OnUriClick(object sender, RoutedEventArgs e)
        { System.Diagnostics.Process.Start(((Hyperlink)sender).NavigateUri.ToString()); }

        private void OnBtnSearchClick(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(SelectedApplication.SearchString))
            {
                
            }
        }

        private void OnKeyPress(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                OnBtnSearchClick(null,null);
            }
        }

        private void OnBtnEmailClick(object sender, RoutedEventArgs e)
        {
            if (SelectedTraceItem != null)
            {
                
            }
        }

        private void OnBtnPrintClick(object sender, RoutedEventArgs e)
        {
            if (SelectedTraceItem == null)
                return;

            var printDialog = new PrintDialog();
            if (printDialog.ShowDialog().GetValueOrDefault(false))
            {
                FlowDocument document = CreatePrintDocument(SelectedTraceItem);
                document.ColumnWidth = printDialog.PrintableAreaWidth;
                DocumentPaginator documentPaginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
                printDialog.PrintDocument(documentPaginator, "TraceView Log");
            }
        }

        private static FlowDocument CreatePrintDocument(TraceItem traceItem)
        {
            var doc = new FlowDocument {
                FontFamily = new FontFamily("Consolas"),
                FontSize = 12,
            };

            var para = new Paragraph(new Run(traceItem.ApplicationName)) {
                FontSize = 24,
                FontWeight = FontWeights.Bold
            };
            doc.Blocks.Add(para);

            PrintKeyValue(doc, "User", traceItem.UserName);
            PrintKeyValue(doc, "Source", traceItem.Source);
            PrintKeyValue(doc, "Severity", traceItem.Severity.ToString());
            PrintKeyValue(doc, "Timestamp", traceItem.TimeStamp.ToString());

            var printable = traceItem.View as IPrintable;
            if (printable != null)
                printable.Print(doc);

            return doc;
        }

        private static void PrintKeyValue(FlowDocument doc, string key, string value, params object[] args)
        {
            var para = new Paragraph {
                FontSize = 10,
                TextIndent = 20d
            };
            para.Inlines.Add(new Run(key) { FontWeight = FontWeights.Bold });
            para.Inlines.Add(": ");
            para.Inlines.Add(string.Format(value, args));
            doc.Blocks.Add(para);
        }

        private void OnBtnDeleteListClick(object sender, RoutedEventArgs e)
        {
            if (SelectedApplication != null)
            {
                if (ItemStack.SelectedItem == TabRecentItems)
                    SelectedApplication.RecentItems.Clear();
                else if (ItemStack.SelectedItem == TabPinnedItems)
                    SelectedApplication.PinnedItems.Clear();
                else if (ItemStack.SelectedItem == TabAllItems)
                    SelectedApplication.AllItems.Clear();
            }
        }

        private void OnBtnPauseChecked(object sender, RoutedEventArgs e)
        {
            ServerImage = TryFindResource("Orange") as ImageSource;
            StatusText = "Server Paused";
        }

        private void OnBtnPauseUnChecked(object sender, RoutedEventArgs e)
        {
            ServerImage = TryFindResource("Green") as ImageSource;
            StatusText = "Server Running";
        }

        private void LoadState()
        {
            MaxDuration = Settings.Instance.MaxDuration;
            MaxItems = Settings.Instance.MaxItems;
            CanShowNotification = Settings.Instance.ShowNotifications;
            Level = Settings.Instance.Level;
            SearchString = Settings.Instance.SearchString;

            switch (Settings.Instance.ThemeColor)
            {
                case "default":
                    brDefault.IsChecked = true;
                    break;
                case "blue":
                    brBlue.IsChecked = true;
                    break;
                case "pink":
                    brPink.IsChecked = true;
                    break;
                case "green":
                    brGreen.IsChecked = true;
                    break;
                case "yellow":
                    brYellow.IsChecked = true;
                    break;                    
            }

            foreach (Severity severityFilter in Settings.Instance.SeverityFilters)
            {
                Severity severityFilterCopy = severityFilter;
                SeverityInfo matchingSeverity = Severities.FirstOrDefault(severity => severityFilterCopy == severity.Severity);
                matchingSeverity.IsChecked = matchingSeverity != null;
            }
        }

        private void SaveState()
        {
            Settings.Instance.MaxDuration = MaxDuration;
            Settings.Instance.MaxItems = MaxItems;
            Settings.Instance.ShowNotifications = CanShowNotification;
            Settings.Instance.Level = Level;
            Settings.Instance.SearchString = SearchString;
            Settings.Instance.ThemeColor = Theme;

            Settings.Instance.SeverityFilters.Clear();
            foreach (SeverityInfo severity in Severities)
            {
                if (severity.IsChecked)
                    Settings.Instance.SeverityFilters.Add(severity.Severity);
            }
        }

        internal void Unload()
        {
            OnUnloaded(null, null);
        }

        private void OnBtnAddNewProfileClick(object sender, RoutedEventArgs e)
        {
            GetRegisteredListeners();
            NewProfile = new Profile {CreationDate = DateTime.Now};
            txtValidationMessage.Text = string.Empty;
            step1.IsChecked = true;
        }

        private void OnBtnOpenFileDialogClick(object sender, RoutedEventArgs e)
        {            
            var dlg = new Microsoft.Win32.OpenFileDialog
                          {
                              DefaultExt = ".config",
                              Filter = "Config Files (.config)|*.config"
                          };          
            var result = dlg.ShowDialog();
            if (result != true) return;
            string filename = dlg.FileName;
            txtConfigPath.Text = filename;
        }

        private void OnBtnWizardNextClick(object sender, RoutedEventArgs e)
        {
            IsGeneratingMode = true;
            step2.IsChecked = true;
            //TODO Code to call listener definition to add configuration
            IsGeneratingMode = false;
        }

        private void OnBtnWizardPreviousClick(object sender, RoutedEventArgs e)
        {
            step1.IsChecked = true;
        }

        private void OnBtnSaveProfileClick(object sender, RoutedEventArgs e)
        {
            //TODO---- actual saving of profile 
            string validationMessage = Profiles.Validate(NewProfile);
            if(string.IsNullOrEmpty(validationMessage))
            {
                Profiles.Add(NewProfile);
                var showProfiles = TryFindResource("ShrinkStoryboard") as Storyboard;
                if(showProfiles!=null)
                    showProfiles.Begin();
            }
                
            else
            {
                txtValidationMessage.Text = validationMessage;
            }
        }

        private void OnLoadProfilesClick(object sender, RoutedEventArgs e)
        {
            LoadProfiles(Profiles.Where(profile => profile.IsChecked));
        }

        private void OnBtnDeleteProfilesClick(object sender, RoutedEventArgs e)
        {
            for (int i = Profiles.Count-1; i >=0 ; i--)
            {
                if(Profiles[i].IsChecked)
                    Profiles.RemoveAt(i);
            }
        }

        private void GetSavedProfiles()
        {
           
        }

        private void GetRegisteredListeners()
        {
            
        }

        private void LoadProfiles(IEnumerable<Profile> profiles)
        {
            
        }
        #endregion

        
    }
}